#include "Level.h"
#include "aStar.h"
#include <list>
#include "HouseCube.h"
#include "PipeCube.h"
#include "ObstacleCube.h"

Level::Level()
{
	srand( time(NULL) );

	levelTime = 0.0;

	difficulty = rand()%4 + 1;
	rows = rand()%15 + 6;
	columns = rand()%15 + 6;

	/*
	//DEBUGGING
	rows = 9;
	columns = 5;
*/

	//mCubeEntity = sceneMgr->createEntity(type + sID.str(), "MasterCube.mesh");

	cubePtr = Ogre::MeshManager::getSingleton().load("MasterCube.mesh", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);

	area = rows*columns;
	score = 0;
	grid = new Grid(rows, columns);
	currentCube = NULL;
	previousCube = NULL;
	nextCube = NULL;
	setupMaterials();
	generateHouseAndObstacleNums();
	//List that tells where houses should be located
	std::list<GridNode*> points = createPath();
	//List that tells where obstacles should be located
	std::list<GridNode*> obstaclePoints = generateObstaclePoints();
	setupLevel( &points, &obstaclePoints );
	placeStartAndEndPipes();
	updateCurrentCube(start);
	finished = false;
}

Level::Level(int difficulty)
{
	srand( time(NULL) );

	levelTime = 0.0;

	this->difficulty = difficulty;
	rows = rand()%15 + 6;
	columns = rand()%15 + 6;
/*
	//DEBUGGING
	rows = 9;
	columns = 5;
*/

	cubePtr = Ogre::MeshManager::getSingleton().load("MasterCube.mesh", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
	score = 0;
	grid = new Grid(rows, columns);
	currentCube = NULL;
	previousCube = NULL;
	nextCube = NULL;
	setupMaterials();
	generateHouseAndObstacleNums();
	std::list<GridNode*> points = createPath();
	std::list<GridNode*> obstaclePoints = generateObstaclePoints();
	setupLevel( &points, &obstaclePoints );
	placeStartAndEndPipes();
	updateCurrentCube(start);
	finished = false;
}

Level::Level(int difficulty, int rows, int columns)
{
	srand( time(NULL) );

	levelTime = 0.0;

	if(rows < 6 || rows > 20)	//Reassign if the user of this constructor decides to make a level too small or large
		rows = rand()%15 + 6;
	if(columns < 6 || columns > 20)
		columns = rand()%15 + 6; 

	cubePtr = Ogre::MeshManager::getSingleton().load("MasterCube.mesh", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);

//mCubeEntity = sceneMgr->createEntity(type + sID.str(), cubePtr->getName()/*"MasterCube.mesh"*/, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);

	this->difficulty = difficulty;
	this->rows = rows;
	this->columns = columns;
	score = 0;
	grid = new Grid(rows, columns);
	currentCube = NULL;
	previousCube = NULL;
	nextCube = NULL;
	setupMaterials();
	generateHouseAndObstacleNums();
	std::list<GridNode*> points = createPath();
	std::list<GridNode*> obstaclePoints = generateObstaclePoints();
	setupLevel( &points, &obstaclePoints );
	placeStartAndEndPipes();
	updateCurrentCube(start);
	finished = false;
}

Level::~Level()
{
	delete(grid);
	delete(begin);
	delete(finish);
}

void Level::setupMaterials()
{
	using namespace Ogre;

	//materials.max_size

	String materialNames[] =
		{"NW2NE", "NW2E", "NW2SW", "NW2S", "NW2SE", "N2W", "N2E", 
		"N2SW", "N2S", "N2SE", "NE2W", "NE2SW", "NE2S", "NE2SE", 
		"W2E", "W2S", "W2SE", "E2SW", "E2S", "SW2SE"};

	//std::list<Ogre::MaterialPtr>::iterator it;
	//for(it = materials.begin(); it != materials.end(); it++)
	for (int i = 0; i < NUM_MATERIALS; i++)
	{
		materials.push_back(MaterialManager::getSingleton().getByName(materialNames[i]));
		materials.back()->load();
	}
}

void Level::generateHouseAndObstacleNums()
{	
	
	//smaller difficulty OR smaller area (based on rows and cols) = less houses
	//smaller difficulty OR smaller area (based on rows and cols) = less obstacles
	//Randoms numbers range from 0-2. Then the numbers are offset according to difficulty
	//The total obstacle and house Range is from 0-11
	int offset = difficulty * 3;//3,6,9,12
	
	//0*3 = 0 ,0*6 = 0, 0*9 = 0, 0*12 = 0
	//1*3 = 3 ,1*6 = 6, 1*9 = 9, 1*12 = 12
	//2*3 = 6 ,2*6 = 12, 2*9 = 18, 2*12 = 24
	
	numHouses = rand()%3 * offset + 1;
	numObstacles = rand()%3 * offset + 1;

	bool checkNumHouses = true;
	bool checkNumObstacles = true;
	//min (6*6)/18 = 2 
	//max (18*18)/18 = 18 
	int numCheck = int(area/18);

	//The area is then accounted for to determine if the houseRand or the obstacleRand is too
	//	many houses for the preset area

	while(checkNumHouses || checkNumObstacles)
	{
		if(numHouses > 10)
			numHouses = 10;
		if(numHouses > numCheck)
			numHouses -= 1;
		else if(numHouses < 2)
			numHouses = 2;	
		else
			checkNumHouses = false;

		
		if(numObstacles > 10)
			numObstacles = 10;
		if(numObstacles > numCheck)
			numObstacles -=1;
		else if(numObstacles < 2)
			numObstacles = 2;
		else
			checkNumObstacles = false;
	}
	

}

std::list<GridNode*> Level::createPath()
{
	std::list<GridNode*> points;
	
	/**************************************** Find the starting row/column **********************************************/
	int startRow = rand()%rows;		//used to determine the starting row of the puzzle
	int startColumn = rand()%columns;	//used to determine the starting column of the puzzle

	if( rand()%2 )	//if 1, rows wins, the start will be on either the left or right side
	{
		if( !(startColumn == 0 || startColumn == columns-1) )	//we already have a valid side that we're going to place the start at
		{
			if( !(rand()%2) )	//if 0, then we'll make the start on the left side, else we'll make it on the right side
				startColumn = 0;
			else
				startColumn = columns-1;
		}
	}
	else	//Columns win, the start will either be on the top or the bottom
	{
		if( rand()%2 )	//if 1, the we'll make the start on the top, else we'll make it start on the bottom
			startRow = 0;
		else
			startRow = rows-1;
	}
	grid->getNode(startRow, startColumn)->setOnPath(true);
	points.push_back( grid->getNode(startRow, startColumn) );
	
	/***************************************Find the end row/column************************************************/
	int endRow = rand()%rows;			//used to determine the end row of the puzzle
	int endColumn = rand()%columns;		//used to determine the end column of the puzzle
	
	do
	{
		if( rand()%2 )	//if 1, rows wins, the start will be on either the left or right side
		{
			if( !(endColumn == 0 || endColumn == columns-1) )	//we already have a valid side that we're going to place the start at
			{
				if( !(rand()%2) )	//if 0, then we'll make the start on the left side, else we'll make it on the right side
					endColumn = 0;
				else
					endColumn = columns-1;
			}
		}
		else	//Columns win, the start will either be on the top or the bottom
		{
			if( rand()%2 )	//if 1, the we'll make the start on the top, else we'll make it start on the bottom
				endRow = 0;
			else
				endRow = rows-1;
		}
	} while( endRow == startRow && endColumn == startColumn );
	GridNode* endNode = grid->getNode(endRow, endColumn);
	endNode->setOnPath(true);

	/**********************************Find the points that will be on the path************************************/
	for( int i = 0; i < numHouses; i++ )
	{
		GridNode* current = grid->getNode(rand()%rows, rand()%columns);
		while( current->equals(endNode) )
			current = grid->getNode(rand()%rows, rand()%columns);

		std::list<GridNode*>::iterator it;
		for(it = points.begin(); it != points.end(); it++)
		{
			if( current->equals(*it) )
			{
				current = grid->getNode(rand()%rows, rand()%columns);
				it = points.begin();
				while( current->equals(*it) ) //Need to check the first one again, since it won't be checked during next iteration of loop
					current = grid->getNode(rand()%rows, rand()%columns);
			}
		}

		current->setOnPath(true);
		points.push_back(current);
	}
	points.push_back(endNode);

	/*************************************************** Remove redundant points *****************************************************/
	std::list<GridNode*>::iterator it;
	std::list<GridNode*>::iterator it2;
	for(it = points.begin(); it != --points.end(); it++)
	{
		it2 = ++it;
		--it;
		
		while( it2 != points.end() )
		{
			if( (*it)->equals(*it2) )
			{
				points.erase( it2 );
				it = points.begin();
				break;
			}
			else
				it2++;
		}
	}

	/*****************************Check to make sure that each point is at least one block away***************************************/
	int numTimesThrough = 0;
	for( it = ++points.begin(); it != --points.end() && it != points.end() && numTimesThrough <= 1000; it++ ) //We never want to move the start or end nodes, so we leave them out of it
	{
		for( it2 = points.begin(); it2 != points.end() && numTimesThrough <= 1000; it2++, numTimesThrough++ )
		{
			if( it2 == it && it2 != points.end() ) //It would be bad to move a house around, especially when comparing to itself!
				it2++;
			else if ( it2 == it && it2 == points.end() ) //They're both at the end; break out or we're doomed!
				break;

			//if( (*it)->equals(*it2) )
			//{
			//	points.erase(it);
			//	it = points.begin();
			//	break;
			//}

			if( grid->getDistance(*it, *it2) <= 20 )	//We're using Manhattan distance, so a node could be 20 points away and be connected to another house
			{											//Adjacent nodes (to the north, east, south, west, are all 10 points away in Manhattan distance
				GridNode* n = grid->getNorthNode(*it);
				GridNode* ne = grid->getNENode(*it);
				GridNode* e = grid->getEastNode(*it);
				GridNode* se = grid->getSENode(*it);
				GridNode* s = grid->getSouthNode(*it);
				GridNode* sw = grid->getSWNode(*it);
				GridNode* w = grid->getWestNode(*it);
				GridNode* nw = grid->getNWNode(*it);

				bool isN = ( n && n->equals(*it2) )? true : false;		//Check where *it2 is in relation to *it
				bool isNE = ( ne && ne->equals(*it2) )? true: false;
				bool isE = ( e && e->equals(*it2) )? true: false;
				bool isSE = ( se && se->equals(*it2) )? true : false;
				bool isS = ( s && s->equals(*it2) )? true : false;
				bool isSW = ( sw && sw->equals(*it2) )? true : false;
				bool isW = ( w && w->equals(*it2) )? true : false;
				bool isNW = ( nw && nw->equals(*it2) )? true: false;

				if( n && !n->isOnPath() && (isS || isSW || isSE) ) //We can still bounce back between two points if the conditions are right :(
				{
					GridNode* tn = grid->getNorthNode(n);
					GridNode* tne = grid->getNENode(n);
					GridNode* tnw = grid->getNWNode(n);

					if(tn && tn->isOnPath()) //There's another House there, we need to put this one somewhere else so that it won't
					{						 //bounce between the two houses
						if( tne && !tne->isOnPath() ) //Don't care which way we go first, either one has the same probability at
						{							  //being as good as the other
							points.insert(it, tne);
							points.erase(++it);
							it = points.begin();
							break;
						}
						else if( tnw && !tnw->isOnPath() )
						{
							points.insert(it, tnw);
							points.erase(++it);
							it = points.begin();
							break;
						}
					}
					if( tnw && tnw->isOnPath() )
					{
						if(tne && !tne->isOnPath())
						{
							points.insert(it, tne);
							points.erase(++it);
							it = points.begin();
							break;
						}
						else if(tn && !tn->isOnPath()) //the worse choice of the two
						{
							points.insert(it, tn);
							points.erase(++it);
							it = points.begin();
							break;
						}
					}
					if( tne && tne->isOnPath() )
					{
						if( tnw && !tnw->isOnPath() )
						{
							points.insert(it, tnw);
							points.erase(++it);
							it = points.begin();
							break;
						}
						else if(tn && !tn->isOnPath())
						{
							points.insert(it, tn);
							points.erase(++it);
							it = points.begin();
							break;
						}
					}
					//else just insert it where we are
					//points.insert(it, n);
					//points.erase(++it);
					//it = points.begin();
					//break;
				}


				if ( ne && !ne->isOnPath() && (isW || isSW || isNW || isS || isSE) )
				{
					GridNode* te = grid->getEastNode(ne);
					GridNode* tne = grid->getNENode(ne);
					GridNode* tn = grid->getNorthNode(ne);

					if(tn && tn->isOnPath()) //There's another House there, we need to put this one somewhere else so that it won't
					{						 //bounce between the two houses
						if( te && !te->isOnPath() ) //Don't care which way we go first, either one has the same probability at
						{							  //being as good as the other
							points.insert(it, te);
							points.erase(++it);
							it = points.begin();
							break;
						}
						else if( tne && !tne->isOnPath() )
						{
							points.insert(it, tne);
							points.erase(++it);
							it = points.begin();
							break;
						}
					}
					if( tne && tne->isOnPath() )
					{
						if(te && !te->isOnPath())
						{
							points.insert(it, te);
							points.erase(++it);
							it = points.begin();
							break;
						}
						else if(tn && !tn->isOnPath()) //the worse choice of the two
						{
							points.insert(it, tn);
							points.erase(++it);
							it = points.begin();
							break;
						}
					}
					if( te && te->isOnPath() )
					{
						if( tn && !tn->isOnPath() )
						{
							points.insert(it, tn);
							points.erase(++it);
							it = points.begin();
							break;
						}
						else if( tne && !tne->isOnPath() )
						{
							points.insert(it, tn);
							points.erase(++it);
							it = points.begin();
							break;
						}
					}
					
					points.insert(it, ne);
					points.erase(++it);
					it = points.begin();
					break;
				}
				

				if ( e && !e->isOnPath() && (isW || isNW || isSW))
				{
					GridNode* te = grid->getEastNode(e);
					GridNode* tse = grid->getSENode(e);
					GridNode* tne = grid->getNENode(e);

					if(te && te->isOnPath()) //There's another House there, we need to put this one somewhere else so that it won't
					{						 //bounce between the two houses
						if( tse && !tse->isOnPath() )
						{
							points.insert(it, tse);
							points.erase(++it);
							it = points.begin();
							break;
						}
						else if( tne && !tne->isOnPath() )
						{
							points.insert(it, tne);
							points.erase(++it);
							it = points.begin();
							break;
						}
					}
					if( tne && tne->isOnPath() )
					{
						if(tse && !tse->isOnPath())
						{
							points.insert(it, tse);
							points.erase(++it);
							it = points.begin();
							break;
						}
						else if(te && !te->isOnPath()) //the worse choice of the two
						{
							points.insert(it, te);
							points.erase(++it);
							it = points.begin();
							break;
						}
					}
					if( tse && tse->isOnPath() )
					{
						if( tne && !tne->isOnPath() )
						{
							points.insert(it, tne);
							points.erase(++it);
							it = points.begin();
							break;
						}
						else if( te && !te->isOnPath() )
						{
							points.insert(it, te);
							points.erase(++it);
							it = points.begin();
							break;
						}
					}

					//points.insert(it, e);
					//points.erase(++it);
					//it = points.begin();
					//break;
				}

				if( se && !se->isOnPath() && (isSW || isW || isNW || isN || isNE) )
				{
					GridNode* tse = grid->getSENode(se);
					GridNode* te = grid->getEastNode(se);
					GridNode* ts = grid->getSouthNode(se);

					if(tse && tse->isOnPath()) //There's another House there, we need to put this one somewhere else so that it won't
					{						 //bounce between the two houses
						if( ts && !ts->isOnPath() )
						{
							points.insert(it, ts);
							points.erase(++it);
							it = points.begin();
							break;
						}
						else if( te && !te->isOnPath() )
						{
							points.insert(it, te);
							points.erase(++it);
							it = points.begin();
							break;
						}
					}
					if( te && te->isOnPath() )
					{
						if(ts && !ts->isOnPath())
						{
							points.insert(it, ts);
							points.erase(++it);
							it = points.begin();
							break;
						}
						else if(tse && !tse->isOnPath()) //the worse choice of the two
						{
							points.insert(it, tse);
							points.erase(++it);
							it = points.begin();
							break;
						}
					}
					if( ts && ts->isOnPath() )
					{
						if( te && !te->isOnPath() )
						{
							points.insert(it, te);
							points.erase(++it);
							it = points.begin();
							break;
						}
						else if( tse && !tse->isOnPath() )
						{
							points.insert(it, tse);
							points.erase(++it);
							it = points.begin();
							break;
						}
					}

					//points.insert(it, se);
					//points.erase(++it);
					//it = points.begin();
					//break;
				}

				if( s && !s->isOnPath() && (isNE || isN || isNW) )
				{
					GridNode* ts = grid->getSouthNode(s);
					GridNode* tse = grid->getSENode(s);
					GridNode* tsw = grid->getSWNode(s);

					if(ts && ts->isOnPath()) //There's another House there, we need to put this one somewhere else so that it won't
					{						 //bounce between the two houses
						if( tse && !tse->isOnPath() )
						{
							points.insert(it, tse);
							points.erase(++it);
							it = points.begin();
							break;
						}
						else if( tsw && !tsw->isOnPath() )
						{
							points.insert(it, tsw);
							points.erase(++it);
							it = points.begin();
							break;
						}
					}
					if( tse && tse->isOnPath() )
					{
						if(tsw && !tsw->isOnPath())
						{
							points.insert(it, tsw);
							points.erase(++it);
							it = points.begin();
							break;
						}
						else if(ts && !ts->isOnPath()) //the worse choice of the two
						{
							points.insert(it, ts);
							points.erase(++it);
							it = points.begin();
							break;
						}
					}
					if( tsw && tsw->isOnPath() )
					{
						if( tse && !tse->isOnPath() )
						{
							points.insert(it, tse);
							points.erase(++it);
							it = points.begin();
							break;
						}
						else if( ts && !ts->isOnPath() )
						{
							points.insert(it, ts);
							points.erase(++it);
							it = points.begin();
							break;
						}
					}

					//points.insert(it, s);
					//points.erase(++it);
					//it = points.begin();
					//break;
				}

				if( sw && !sw->isOnPath() )
				{
					GridNode* tsw = grid->getSWNode(sw);
					GridNode* ts = grid->getSouthNode(sw);
					GridNode* tw = grid->getWestNode(sw);

					if(tsw && tsw->isOnPath()) //There's another House there, we need to put this one somewhere else so that it won't
					{						 //bounce between the two houses
						if( ts && !ts->isOnPath() )
						{
							points.insert(it, ts);
							points.erase(++it);
							it = points.begin();
							break;
						}
						else if( tw && !tw->isOnPath() )
						{
							points.insert(it, tw);
							points.erase(++it);
							it = points.begin();
							break;
						}
					}
					if( ts && ts->isOnPath() )
					{
						if(tw && !tw->isOnPath())
						{
							points.insert(it, tw);
							points.erase(++it);
							it = points.begin();
							break;
						}
						else if(tsw && !tsw->isOnPath()) //the worse choice of the two
						{
							points.insert(it, tsw);
							points.erase(++it);
							it = points.begin();
							break;
						}
					}
					if( tw && tw->isOnPath() )
					{
						if( ts && !ts->isOnPath() )
						{
							points.insert(it, ts);
							points.erase(++it);
							it = points.begin();
							break;
						}
						else if( tsw && !tsw->isOnPath() )
						{
							points.insert(it, tsw);
							points.erase(++it);
							it = points.begin();
							break;
						}
					}

					//points.insert(it, sw);
					//points.erase(++it);
					//it = points.begin();
					//break;
				}

				if( w && !w->isOnPath() && (isE || isNE || isSE) )
				{
					GridNode* tw = grid->getWestNode(w);
					GridNode* tnw = grid->getNWNode(w);
					GridNode* tsw = grid->getSWNode(w);

					if(tw && tw->isOnPath()) //There's another House there, we need to put this one somewhere else so that it won't
					{						 //bounce between the two houses
						if( tsw && !tsw->isOnPath() )
						{
							points.insert(it, tsw);
							points.erase(++it);
							it = points.begin();
							break;
						}
						else if( tnw && !tnw->isOnPath() )
						{
							points.insert(it, tnw);
							points.erase(++it);
							it = points.begin();
							break;
						}
					}
					if( tnw && tnw->isOnPath() )
					{
						if(tsw && !tsw->isOnPath())
						{
							points.insert(it, tsw);
							points.erase(++it);
							it = points.begin();
							break;
						}
						else if(tw && !tw->isOnPath()) //the worse choice of the two
						{
							points.insert(it, tw);
							points.erase(++it);
							it = points.begin();
							break;
						}
					}
					if( tsw && tsw->isOnPath() )
					{
						if( tnw && !tnw->isOnPath() )
						{
							points.insert(it, tnw);
							points.erase(++it);
							it = points.begin();
							break;
						}
						else if( tw && !tw->isOnPath() )
						{
							points.insert(it, tw);
							points.erase(++it);
							it = points.begin();
							 break;
						}
					}

					//points.insert(it, w);
					//points.erase(++it);
					//it = points.begin();
					//break;
				}

				if( nw && !nw->isOnPath() && (isNE || isE || isSE || isS || isSW) )
				{
					GridNode* tnw = grid->getNWNode(nw);
					GridNode* tn = grid->getNorthNode(nw);
					GridNode* tw = grid->getWestNode(nw);

					if(tnw && tnw->isOnPath()) //There's another House there, we need to put this one somewhere else so that it won't
					{						 //bounce between the two houses
						if( tn && !tn->isOnPath() )
						{
							points.insert(it, tn);
							points.erase(++it);
							it = points.begin();
							 break;
						}
						else if( tw && !tw->isOnPath() )
						{
							points.insert(it, tw);
							points.erase(++it);
							it = points.begin();
							 break;
						}
					}
					if( tw && tw->isOnPath() )
					{
						if(tn && !tn->isOnPath())
						{
							points.insert(it, tn);
							points.erase(++it);
							it = points.begin();
							 break;
						}
						else if(tnw && !tnw->isOnPath()) //the worse choice of the two
						{
							points.insert(it, tnw);
							points.erase(++it);
							it = points.begin();
							 break;
						}
					}
					if( tn && tn->isOnPath() )
					{
						if( tw && !tw->isOnPath() )
						{
							points.insert(it, tw);
							points.erase(++it);
							it = points.begin();
							 break;
						}
						else if( tnw && !tnw->isOnPath() )
						{
							points.insert(it, tnw);
							points.erase(++it);
							it = points.begin();
							 break;
						}
					}

					//points.insert(it, nw);
					//points.erase(++it);
					//it = points.begin();
					//break;
				}

				//Just erase the piece of shit and start from the beginning
				points.erase(it);
				numHouses--;
				it = points.begin();
				break;
			}
		}
	}



	/******************************************Use A* to find the path from point to point********************************************/
	for(it = points.begin(); it != --points.end(); it++)
	{
		Astar a;
		a.setGrid(grid);

		a.setStartNode(*it);
		it2 = ++it;
		a.setGoalNode(*it2);
		it--;

		a.findPath();
		std::list<GridNode*>* subPath = a.getPath();
		std::list<GridNode*>::iterator pIt;
		for(pIt = subPath->begin(); pIt != subPath->end(); pIt++)
		{
			(*pIt)->setOnPath(true);
			path.push_back(*pIt);
		}
	}

	//Remove all redundant points in the path
	for(it = path.begin(); it != --path.end(); it++)
	{
		it2 = ++it;
		--it;
		while(it2 != path.end())
		{
			if( (*it)->equals(*it2) )
			{
				path.erase(it2);
				it = path.begin();
				break;
			}
			else
				it2++;
		}
	}

	return points;
}


std::list<GridNode*> Level::generateObstaclePoints()
{
	std::list<GridNode*> obstaclePoints;

	int numObstaclesGenerated = 0;

	//int numObstacles = 3;

	while(numObstaclesGenerated < numObstacles)
	{
	
		GridNode* current = grid->getNode(rand()%rows, rand()%columns);
		
		if(!current->isOnPath())
		{
			obstaclePoints.push_back(current);
			numObstaclesGenerated++;
		}
	}
	
	return obstaclePoints;
}



void Level::setupLevel( std::list<GridNode*>* points,  std::list<GridNode*>* obstaclePoints)
{

	//load materials
	//MaterialPtr mat = MaterialManager::getSingleton().getByName("HouseCube");


	GridNode* st = points->front();
	GridNode* en = points->back();
	//set up all cubes as pipeCubes
	for( int i = 0; i < rows; i++ )
	{
		for( int j = 0; j < columns; j++ )
		{
			GridNode* g = grid->getNode(i,j);
			int iD = g->getID();
			
			Ogre::Vector3 location = g->getPosition();

			Cube* cube = NULL;
			
			//if we still have some points that can be houses
			if(!points->empty())
			{
				//if this is where we are supposed to put a houseCube
				std::list<GridNode*>::iterator it;
				for(it = points->begin(); it != points->end(); it++)
				{
					if(g->equals(*it))
					{
						if( st && g->equals(st) )
						{
							cube = new PipeCube(location, g, materials, cubePtr, iD);
							st = NULL;
							start = cube;
						}
						else if( en && g->equals(en) )
						{
							cube = new PipeCube(location, g, materials, cubePtr, iD);
							en = NULL;
							end = cube;
							((PipeCube*)cube)->hideAllTextures();
						}
						else
						{
							cube = new HouseCube(location, g, cubePtr, iD);
							//we want a five minute timer on the house cube
							//our delta time is in milliseconds
							//1000milliseconds in a second, 60 seconds in a minute
							
							Ogre::Real fiveMins = 5*(1000*60);
							((HouseCube *)cube)->setHouseTime(fiveMins);
						}

						points->erase(it);
						if(points->empty())
							break;
						it = points->begin();
					}
				}
				//if( !cube )
				//	cube = new PipeCube(location, g, materials, cubePtr, iD);
			}
			if( !cube && !obstaclePoints->empty())
			{
				numObstacles = numObstacles;
				//if this is where we are supposed to put an obstacleCube
				std::list<GridNode*>::iterator it;
				for(it = obstaclePoints->begin(); it != obstaclePoints->end(); it++)
				{
					if((g->getID()) == ((*it)->getID()))
					{
						cube = new ObstacleCube( location, g, cubePtr, iD);
						obstaclePoints->erase(it);
						if(obstaclePoints->empty())
							break;
						it = obstaclePoints->begin();
					}
				}
				
			}
			if( !cube )
			{
				cube = new PipeCube(location, g, materials, cubePtr, iD);
				((PipeCube*)cube)->hideAllTextures();
			}
			
/*
			if(cube == NULL)
				cube = new PipeCube( iD, location );
				*/
			
			//We can just make this part of the loop that creates HouseCubes.  Just iterate over the obstaclePoints like we did points
/*
			//if we still have some obstacle points AND the cube is not a houseCube
			if(!obstaclePoints.empty() && cube == NULL)
				//if this is where we are supposed to put an obstacleCube
				if(iD == obstaclePoints.front())
				{
					cube = new ObstacleCube( iD, location );	
					obstaclePoints.pop_front();
				}
*/			

			cubeList.push_back(cube);
		}
	}
}

std::list<Cube*>* Level::getCubeList()
{
	return &cubeList;
}

void Level::updateCurrentCube(Cube* newCurrent)
{
	previousCube = currentCube;
	currentCube = newCurrent;
	//if( previousCube )
	previousCube->getSceneNode()->showBoundingBox(false);		//Hide the previous's bounding box, we're no longer on that Cube
	//std::string type = (std::string)(typeid(*currentCube).name());
	//if( currentCube && ( type.compare("class PipeCube") == 0 || type.compare("class HouseCube") == 0 ) )
	currentCube->getSceneNode()->showBoundingBox(true);		//Show the new current Cube's bounding box to let the player know which Cube they're working with

	if( previousCube )
	{
		GridNode* previousNode = previousCube->getGridNode();
		if( previousNode )
		{
			GridNode* n = grid->getNorthNode(previousNode);
			GridNode* ne = grid->getNENode(previousNode);
			GridNode* e = grid->getEastNode(previousNode);
			GridNode* se = grid->getSENode(previousNode);
			GridNode* s = grid->getSouthNode(previousNode);
			GridNode* sw = grid->getSWNode(previousNode);
			GridNode* w = grid->getWestNode(previousNode);
			GridNode* nw = grid->getNWNode(previousNode);
			
			GridNode* currentNode = currentCube->getGridNode();
			if( n && n->equals(currentNode) )
				direction = N;
			else if( ne && ne->equals(currentNode) )
				direction = NE;
			else if( e && e->equals(currentNode) )
				direction = E;
			else if( se && se->equals(currentNode) )
				direction = SE;
			else if( s && s->equals(currentNode) )
				direction = S;
			else if( sw && sw->equals(currentNode) )
				direction = SW;
			else if( w && w->equals(currentNode) )
				direction = W;
			else if( nw && nw->equals(currentNode) )
				direction = NW;
		}
	}
}

bool Level::areConnected()
{
	std::string pType;
	if( previousCube )
		pType = (std::string)(typeid(*previousCube).name());

	std::string cType = (std::string)(typeid(*currentCube).name());
	int* pExits;
	int* cExits;
	bool result = false;

	if( (pType.compare("class PipeCube")) == 0 )
		pExits = ((PipeCube*)previousCube)->getCurrentFace();
	else if (pType.compare("class HouseCube") == 0 )
		return true;
	else
		return false;

	if( (cType.compare("class PipeCube")) == 0 )
		cExits = ((PipeCube*)currentCube)->getCurrentFace();
	else if( cType.compare("class HouseCube") == 0 )
		return true;
	else
		return false;

	switch(direction)
	{
		case N:
		{
			if( ((*pExits == 0 && *(pExits+1) == 1) || (*(pExits+2) == 0 && *(pExits+3) == 1)) && 
				((*cExits == 2 && *(cExits+1) == 1) || (*(cExits+2) == 2 && *(cExits+3) == 1)) )	//We need to check both y,x coordinates in order to be sure,
				result = true;																	//as one is an entrance and one is an exit.  We don't care which
			break;
		}
		case NE:
		{
			if( ((*pExits == 0 && *(pExits+1) == 2) || (*(pExits+2) == 0 && *(pExits+3) == 2)) &&
				((*cExits == 2 && *(cExits+1) == 0) || (*(cExits+2) == 2 && *(cExits+3) == 0)) )
				result = true;
			break;
		}
		case E:
		{
			if( ((*pExits == 1 && *(pExits+1) == 2) || (*(pExits+2) == 1 && *(pExits+3) == 2)) &&
				((*cExits == 1 && *(cExits+1) == 0) || (*(cExits+2) == 1 && *(cExits+3) == 0)) )
				result = true;
			break;
		}
		case SE:
		{
			if( ((*pExits == 2 && *(pExits+1) == 2) || (*(pExits+2) == 2 && *(pExits+3) == 2)) &&
				((*cExits == 0 && *(cExits+1) == 0) || (*(cExits+2) == 0 && *(cExits+3) == 0)) )
				result = true;
			break;
		}
		case S:
		{
			if( ((*pExits == 2 && *(pExits+1) == 1) || (*(pExits+2) == 2 && *(pExits+3) == 1)) &&
				((*cExits == 0 && *(cExits+1) == 1) || (*(cExits+2) == 0 && *(cExits+3) == 1)) )
				result = true;
			break;
		}
		case SW:
		{
			if( ((*pExits == 2 && *(pExits+1) == 2) || (*(pExits+2) == 2 && *(pExits+3) == 2)) &&
				((*cExits == 0 && *(cExits+1) == 2) || (*(cExits+2) == 0 && *(cExits+3) == 2)) )
				result = true;
			break;
		}
		case W:
		{
			if( ((*pExits == 1 && *(pExits+1) == 0) || (*(pExits+2) == 1 && *(pExits+3) == 0)) &&
				((*cExits == 1 && *(cExits+1) == 2) || (*(cExits+2) == 1 && *(cExits+3) == 2)) )
				result = true;
			break;
		}
		case NW:
		{
			if( ((*pExits == 0 && *(pExits+1) == 0) || (*(pExits+2) == 0 && *(pExits+3) == 0)) &&
				((*cExits == 2 && *(cExits+1) == 2) || (*(cExits+2) == 2 && *(cExits+3) == 2)) )
				result = true;
			break;
		}
	}

	delete[] pExits;
	delete[] cExits;
	return result;
}

bool Level::nextIsntObstacle()
{
	bool result = false;
	std::string cType = (std::string)(typeid(*currentCube).name());
	int* cExits = NULL;
	if( (cType.compare("class PipeCube")) == 0 )
		cExits = ((PipeCube*)currentCube)->getCurrentFace();
	else if( cType.compare("class HouseCube") == 0)
		nextDirection = direction;
	else
		return false;

	GridNode* nextNode;
	if( cExits )
	{
		switch( direction )
		{
			case N:
				if( *cExits == 2 && *(cExits+1) == 1 ) //We need to know which part of the exits are not connected to the previousCube
				{
					switch(*(cExits+2))
					{
						case 0:
							if( *(cExits+3) == 0 )
								nextDirection = NW;
							else if( *(cExits+3) == 1 )
								nextDirection = N;
							else	//*(cExits+3) == 2
								nextDirection = NE;
							break;
						case 1:
							if( *(cExits+3) == 0 )
								nextDirection = W;
							else	//*(cExits+3) == 2
								nextDirection = E;
							break;
					}
				}
				else	//The other two are going to be connected to the next cube
				{
					switch(*cExits)
					{
						case 0:
							if( *(cExits+1) == 0 )
								nextDirection = NW;
							else if( *(cExits+1) == 1 )
								nextDirection = N;
							else	//*(cExits+1) == 2
								nextDirection = NE;
							break;
						case 1:
							if( *(cExits+1) == 0 )
								nextDirection = W;
							else	//*(cExits+1) == 2
								nextDirection = E;
							break;
					}
				}
				break;
			case NE:
				if( *cExits == 2 && *(cExits+1) == 0 ) //We need to know which part of the exits are not connected to the previousCube
				{
					switch(*(cExits+2))
					{
						case 0:
							if( *(cExits+3) == 0 )
								nextDirection = NW;
							else if( *(cExits+3) == 1 )
								nextDirection = N;
							else	//*(cExits+3) == 2
								nextDirection = NE;
							break;
						case 1:
							nextDirection = E;	//*(cExits+3) == 2 can be the only direction for the pipes to be connected given this situation
							break;
						case 2:
							nextDirection = SE;	//*(cExits+3) == 0 can be the only direction for the pipes to be connected given this situation
							break;
					}
				}
				else
				{
					switch(*cExits)
					{
						case 0:
							if( *(cExits+1) == 0 )
								nextDirection = NW;
							else if( *(cExits+1) == 1 )
								nextDirection = N;
							else	//*(cExits+1) == 2
								nextDirection = NE;
							break;
						case 1:
							nextDirection = E;	//*(cExits+1) == 2 can be the only direction for the pipes to be connected given this situation
							break;
						case 2:
							nextDirection = SE;	//*(cExits+1) == 0 can be the only direction for the pipes to be connected given this situation
							break;
					}
				}
				break;
			case E:
				if( *cExits == 1 && *(cExits+1) == 0 ) //We need to know which part of the exits are not connected to the previousCube
				{
					switch(*(cExits+2))
					{
						case 0:
							if( *(cExits+3) == 1 )
								nextDirection = N;
							else	//*(cExits+3) == 2
								nextDirection = NE;
							break;
						case 1:
							nextDirection = E;	//*(cExits+3) == 2 can be the only direction for the pipes to be connected given this situation
							break;
						case 2:
							if( *(cExits+3) == 1 )
								nextDirection = S;
							else //*(cExits+3) == 2
								nextDirection = SE;
							break;
					}
				}
				else
				{
					switch(*cExits)
					{
						case 0:
							if( *(cExits+1) == 1 )
								nextDirection = N;
							else	//*(cExits+1) == 2
								nextDirection = NE;
							break;
						case 1:
							nextDirection = E;	//*(cExits+1) == 2 can be the only direction for the pipes to be connected given this situation
							break;
						case 2:
							if( *(cExits+1) == 1 )
								nextDirection = S;
							else //*(cExits+1) == 2
								nextDirection = SE;
							break;
					}
				}
				break;
			case SE:
				if( *cExits == 0 && *(cExits+1) == 0 ) //We need to know which part of the exits are not connected to the previousCube
				{
					switch(*(cExits+2))
					{
						case 0:	
							nextDirection = NE;
							break;
						case 1:
							nextDirection = E;	//*(cExits+3) == 2 can be the only direction for the pipes to be connected given this situation
							break;
						case 2:
							if( *(cExits+3) == 0 )
								nextDirection = SW;
							else if( *(cExits+3) == 1 )
								nextDirection = S;
							else //*(cExits+3) == 2
								nextDirection = SE;
							break;
					}
				}
				else
				{
					switch(*cExits)
					{
						case 0:
							if( *(cExits+1) == 1 )
								nextDirection = N;
							else	//*(cExits+1) == 2
								nextDirection = NE;
							break;
						case 1:
							nextDirection = E;	//*(cExits+1) == 2 can be the only direction for the pipes to be connected given this situation
							break;
						case 2:
							if( *(cExits+1) == 1 )
								nextDirection = S;
							else //*(cExits+1) == 2
								nextDirection = SE;
							break;
					}
				}
				break;
			case S:
				if( *cExits == 0 && *(cExits+1) == 1 ) //We need to know which part of the exits are not connected to the previousCube
				{
					switch(*(cExits+2))
					{
						case 1:
							nextDirection = E;	//*(cExits+3) == 2 can be the only direction for the pipes to be connected given this situation
							break;
						case 2:
							if( *(cExits+3) == 0 )
								nextDirection = SW;
							else if( *(cExits+3) == 1 )
								nextDirection = S;
							else //*(cExits+3) == 2
								nextDirection = SE;
							break;
					}
				}
				else
				{
					switch(*cExits)
					{
						case 1:
							nextDirection = E;	//*(cExits+1) == 2 can be the only direction for the pipes to be connected given this situation
							break;
						case 2:
							if( *(cExits+1) == 0 )
								nextDirection = SW;
							else if( *(cExits+1) == 1 )
								nextDirection = S;
							else //*(cExits+1) == 2
								nextDirection = SE;
							break;
					}
				}
				break;
			case SW:
				if( *cExits == 0 && *(cExits+1) == 2 ) //We need to know which part of the exits are not connected to the previousCube
				{
					switch(*(cExits+2))
					{
						case 0:
							nextDirection = NW;	//*(cExits+3) == 0 can be the only direction for the pipes to be connected given this situation
							break;
						case 1:
							nextDirection = W;	//*(cExits+3) == 0 can be the only direction for the pipes to be connected given this situation
							break;
						case 2:
							if( *(cExits+3) == 0 )
								nextDirection = SW;
							else if( *(cExits+3) == 1 )
								nextDirection = S;
							else //*(cExits+3) == 2
								nextDirection = SE;
							break;
					}
				}
				else
				{
					switch(*cExits)
					{
						case 0:
							nextDirection = NW;	//*(cExits+1) == 0 can be the only direction for the pipes to be connected given this situation
							break;
						case 1:
							nextDirection = W;	//*(cExits+1) == 0 can be the only direction for the pipes to be connected given this situation
							break;
						case 2:
							if( *(cExits+1) == 0 )
								nextDirection = SW;
							else if( *(cExits+1) == 1 )
								nextDirection = S;
							else //*(cExits+1) == 2
								nextDirection = SE;
							break;
					}
				}
				break;
			case W:
				if( *cExits == 1 && *(cExits+1) == 2 ) //We need to know which part of the exits are not connected to the previousCube
				{
					switch(*(cExits+2))
					{
						case 0:
							if( *(cExits+3) == 0 )
								nextDirection = NW;
							else	//*(cExits+3) == 1
								nextDirection = N;
							break;
						case 1:
							nextDirection = W;	//*(cExits+3) == 0 can be the only direction for the pipes to be connected given this situation
							break;
						case 2:
							if( *(cExits+3) == 0 )
								nextDirection = SW;
							else	//*(cExits+3) == 1
								nextDirection = S;
							break;
					}
				}
				else
				{
					switch(*cExits)
					{
						case 0:
							if( *(cExits+1) == 0 )
								nextDirection = NW;
							else	//*(cExits+1) == 1
								nextDirection = N;
							break;
						case 1:
							nextDirection = W;	//*(cExits+1) == 0 can be the only direction for the pipes to be connected given this situation
							break;
						case 2:
							if( *(cExits+1) == 0 )
								nextDirection = SW;
							else	//*(cExits+1) == 1
								nextDirection = S;
							break;
					}
				}
				break;
			case NW:
				if( *cExits == 2 && *(cExits+1) == 2 ) //We need to know which part of the exits are not connected to the previousCube
				{
					switch(*(cExits+2))
					{
						case 0:
							if( *(cExits+3) == 0 )
								nextDirection = NW;
							else if( *(cExits+3) == 1 )
								nextDirection = N;
							else	//*(cExits+3) == 2
								nextDirection = NE;
							break;
						case 1:
							nextDirection = W;	//*(cExits+3) == 0 can be the only direction for the pipes to be connected given this situation
							break;
						case 2:
							nextDirection = SW;	//*(cExits+3) == 0 can be the only direction for the pipes to be connected given this situation
							break;
					}
				}
				else
				{
					switch(*cExits)
					{
						case 0:
							if( *(cExits+1) == 0 )
								nextDirection = NW;
							else if( *(cExits+1) == 1 )
								nextDirection = N;
							else	//*(cExits+1) == 2
								nextDirection = NE;
							break;
						case 1:
							nextDirection = W;	//*(cExits+1) == 0 can be the only direction for the pipes to be connected given this situation
							break;
						case 2:
							nextDirection = SW;	//*(cExits+1) == 0 can be the only direction for the pipes to be connected given this situation
							break;
					}
				}
				break;
		}
	}

	switch(nextDirection)
	{
		case N:
			nextNode = grid->getNorthNode(currentCube->getGridNode());
			break;
		case NE:
			nextNode = grid->getNENode(currentCube->getGridNode());
			break;
		case E:
			nextNode = grid->getEastNode(currentCube->getGridNode());
			break;
		case SE:
			nextNode = grid->getSENode(currentCube->getGridNode());
			break;
		case S:
			nextNode = grid->getSouthNode(currentCube->getGridNode());
			break;
		case SW:
			nextNode = grid->getSWNode(currentCube->getGridNode());
			break;
		case W:
			nextNode = grid->getWestNode(currentCube->getGridNode());
			break;
		case NW:
			nextNode = grid->getNWNode(currentCube->getGridNode());
	}

	if( nextNode )	//We're probably off the map, we'll consider it an obstacle
	{
		std::list<Cube*>::iterator it;
		for( it	= cubeList.begin(); it != cubeList.end(); it++ ) //ITERATE THROUGH THE CUBELIST TO FIND THE CUBE THAT HAS THE SAME GRIDNODE AS NEXTNODE
		{					//Yes, this could be done a lot better, but I don't think it'll be a huge performance hit.  We'll deal with it for now.
			if( (*it)->getGridNode()->equals(nextNode) )	//We've found the Cube that is the next one, now we need to assign nextNode if it isn't an ObstacleCube
			{
				if( ((std::string)(typeid(*(*it)).name())).compare("class ObstacleCube") != 0 )
				{
					nextCube = *it;
					result = true;
				}

				break;
			}
		}
	}

	return result;
}

void Level::update(Ogre::Real deltaTime)
{
	levelTime += deltaTime;
	Ogre::Real levelMinutes = levelTime/(1000*60);
	if( currentCube->getGridNode()->equals(end->getGridNode()) )
		finished = true;
	currentCube->update(deltaTime);
	previousCube->update(deltaTime);
}

Cube* Level::getStartCube()
{
	return start;
}

void Level::placeStartAndEndPipes()
{
	int* sCoords = grid->findGridCoords(start->getGridNode());		//sCoords is ordered *sCoords = y and *(sCoords+1) = x
	Ogre::Vector3 location = start->getGridNode()->getPosition();
	if( *sCoords == 0 )	//Place the pipe above this one, it is at the top
	{
		location.z -= 10;
		direction = S;
	}
	else if( *sCoords == rows-1 )	//Place the pipe below this one, it is at the bottom
	{
		location.z += 10;
		direction = N;
	}
	else if( *(sCoords+1) == 0 )	//Place the pipe to the left of this one, it is at the left
	{
		location.x -= 10;
		direction = E;
	}
	else	//*(sCoords+1) == columns-1		//Place the pipe to the right of this one, it is at the right
	{
		location.x += 10;
		direction = W;
	}

	currentCube = new PipeCube(location, NULL, materials, cubePtr, -1, NULL, direction);
	begin = currentCube;

	int* eCoords = grid->findGridCoords(end->getGridNode());
	location = end->getGridNode()->getPosition();
	int tDirection;
	if( *eCoords == 0 )
	{
		tDirection = S;
		location.z -= 10;
	}
	else if( *eCoords == rows-1 )
	{
		tDirection = N;
		location.z += 10;
	}
	else if( *(eCoords+1) == 0 )
	{
		tDirection = E;
		location.x -= 10;
	}
	else	//*(eCoords+1) == columns-1
	{
		tDirection = W;
		location.x += 10;
	}

	finish = new PipeCube(location, NULL, materials, cubePtr, -2, NULL, tDirection);
	delete[] sCoords;
	delete[] eCoords;
}

Cube* Level::checkNextConnectivity()
{
	if( areConnected() && nextIsntObstacle() )
	{
		updateCurrentCube(nextCube);
		if( currentCube == end )
			finished = true;
		nextCube = NULL;
		std::string currType = (std::string)(typeid(*currentCube).name());
		if( currType.compare("class HouseCube") == 0)//They connected to a House Cube, for now this will be the scoring
			score += 100;
		else	//They connected to a PipeCube, for now this will be the scoring
		{
			((PipeCube*)currentCube)->showAllTextures();
			score += 20;
		}
	}

	return currentCube;
}